package cc.creativecomputing.resonate;

import cc.creativecomputing.CCApp;
import cc.creativecomputing.CCApplicationManager;
import cc.creativecomputing.events.CCKeyEvent;
import cc.creativecomputing.graphics.CCCamera;
import cc.creativecomputing.graphics.CCDrawMode;
import cc.creativecomputing.graphics.CCVBOMesh;
import cc.creativecomputing.graphics.shader.CCGLSLShader;
import cc.creativecomputing.graphics.shader.CCShaderBuffer;
import cc.creativecomputing.graphics.texture.CCTexture2D;
import cc.creativecomputing.graphics.texture.CCTextureIO;
import cc.creativecomputing.graphics.texture.CCTexture.CCTextureTarget;
import cc.creativecomputing.io.CCIOUtil;
import cc.creativecomputing.math.CCMath;
import cc.creativecomputing.math.CCVector3f;

/**
 * This class demonstrates the basic particle system pipeline. 
 * The data fbos are set up and we use the emit shader to create
 * particles with a given position, lifetime and initial speed.
 * There is no velocity udpate yet.
 * The display shader takes the particle position texture and displaces 
 * vertices according to the positions. In the display shader the
 * lifetime is evaluated to assign a color.
 * @author maxg
 *
 */
public class ParticleAppEmitter_01a extends CCApp{
	
	// two particle data buffers (actual and last timestep)
	private CCShaderBuffer _myDataBuffer;
	private CCShaderBuffer _myLastDataBuffer;
	
	// emit paricles
	private CCGLSLShader   _myEmitShader;
	
	// update the positions
	private CCGLSLShader   _myUpdateShader;
	
	// render as vertices
	private CCGLSLShader   _myDisplayShader;
	private CCVBOMesh _myMesh;
	
	
	private CCCamera  _myCamera;
	
	// size of the system
	private int particleW = 256;
	private int particleH = 256;
	
	@Override
	public void setup() {
		
		_myCamera = new CCCamera(g);
		_myCamera.position().x = 0;
		_myCamera.position().y = 0;
		_myCamera.position().z =150;
		_myCamera.target(new CCVector3f(0,0,0));
		
		_myEmitShader = new CCGLSLShader (null, CCIOUtil.classPath(this, "shader01/emit_sphere_fp.glsl"));
		_myEmitShader.load();
		
		_myDisplayShader = new CCGLSLShader (CCIOUtil.classPath(this, "shader01/display_vp.glsl"), 
				  							 CCIOUtil.classPath(this, "shader01/display_fp.glsl"));
		_myDisplayShader.load();
		
	
		_myUpdateShader = new CCGLSLShader (null, CCIOUtil.classPath(this, "shader01/update_fp.glsl"));
		_myUpdateShader.load();
		
		
		_myMesh = new CCVBOMesh(CCDrawMode.POINTS, particleW*particleH);
		for (int i=0; i<particleW; i++) {
			for (int j=0; j<particleH; j++) {
				_myMesh.addVertex((float)i/particleW, (float)j/particleH);
			}
		}
		
		_myDataBuffer = new CCShaderBuffer (32, 3, 4, particleW, particleH, CCTextureTarget.TEXTURE_2D);
		_myDataBuffer.clear();
		_myLastDataBuffer = new CCShaderBuffer (32, 3, 4, particleW, particleH, CCTextureTarget.TEXTURE_2D);
		_myLastDataBuffer.clear();
		
		g.pointSize(1);
		g.noBlend();
	}

	private void swap() {
		CCShaderBuffer tmp = _myDataBuffer;
		_myDataBuffer = _myLastDataBuffer;
		_myLastDataBuffer = tmp;
	}
	
	@Override
	public void update (float theDeltaTime) {
		
		_myEmitShader.start();

		g.texture (0, _myLastDataBuffer.attachment(0));
		g.texture (1, _myLastDataBuffer.attachment(1));
		g.texture (2, _myLastDataBuffer.attachment(2));
		
		_myEmitShader.uniform1i("positions", 0);
		_myEmitShader.uniform1i("velocities", 1);
		_myEmitShader.uniform1i("infos", 2);
		_myEmitShader.uniform3f("randomSeed", CCMath.random(100f), CCMath.random(100f),  CCMath.random(3000,10000));
		
		_myDataBuffer.draw();
		_myEmitShader.end();
		g.noTexture();
		
		swap();
		
		_myUpdateShader.start();
		g.texture (0, _myLastDataBuffer.attachment(0));
		g.texture (1, _myLastDataBuffer.attachment(1));
		g.texture (2, _myLastDataBuffer.attachment(2));
		
		_myUpdateShader.uniform1i("positions", 0);
		_myUpdateShader.uniform1i("velocities", 1);
		_myUpdateShader.uniform1i("infos", 2);
		_myUpdateShader.uniform1i("constraints", 3);
		
		_myUpdateShader.uniform1f("deltaTime", theDeltaTime);
		
		_myDataBuffer.draw();
		_myUpdateShader.end();
		g.noTexture();
		
		swap();
	}
	
	@Override
	public void keyPressed (CCKeyEvent theKeyEvent) {
		if (theKeyEvent.keyChar()=='w') {
			_myCamera.position().z -= 10;
		}
		if (theKeyEvent.keyChar()=='s') {
			_myCamera.position().z += 10;
		}
		if (theKeyEvent.keyChar()=='e') {
			_myCamera.rotateXaroundTarget(0.01f);
		}
	};
	private void updateCam() {
		_myCamera.near(0.1f);
		_myCamera.far(1000f);
			
		_myCamera.xRotation(0);
		_myCamera.yRotation(0);
		_myCamera.zRotation(0);
			
		_myCamera.fov(45);
	}
	
	@Override
	public void draw() {
		
		updateCam();
		_myCamera.draw(g);
		
		g.clearColor(0f);
		g.clear();
		g.pointSize(1);
		_myDisplayShader.start();
		g.texture (0, _myLastDataBuffer.attachment(0));
		g.texture (1, _myLastDataBuffer.attachment(2));
		_myDisplayShader.uniform1i("positions", 0);
		_myDisplayShader.uniform1i("infos", 1);
		_myMesh.draw(g);
		_myDisplayShader.end();
		g.noTexture();
		
	}
	
	public static void main (String[] args) {
		CCApplicationManager myManager = new CCApplicationManager(ParticleAppEmitter_01a.class);
		myManager.settings().size(1000, 1000);
		myManager.settings().display(0);
		myManager.settings().antialiasing(8);
		myManager.settings().fov(30);
		myManager.settings().antialiasing(10);
		myManager.start();
	}
}
